Skip to main content

Http Server 驱动

该驱动会启动一个 http server, 允许每个 注册一个 路径. 客户端通过不同的 路径 向不同的表推送数据.

驱动配置说明

驱动配置

  1. 请求路径: 定义当前表接收数据的访问路径, 可以包含多层级的路径. 例如: /data, /api/data 等, 也可以使用占位符 /api/data/{deviceId}, 占位符的值可以从 request.pathVariables 获取.
  2. 前缀匹配: 启用后, 可接收所有请求路径以该路径为前缀的请求. 例如: /api/data, 可以接收 /api/data , /api/data/1, /api/data/device/设备编号1 等请求.
  3. 请求方式: 即接收哪些请求方式, 可多选.
  4. 是否注册不带前缀的请求路径: 不勾选该选项时, 驱动默认创建的接口路径默认带有 /http-server-driver 前缀. 例如, 设置的 请求路径/device/data, 实际访问路径为 /http-server-driver/device/data. 如果勾选了该选项后, 会同时注册 /http-server-driver/device/data/device/data 两个访问路径. 可通过 http://平台IP:3030/http-server-driver/device/datahttp://平台IP:驱动端口/http-server-driver/device/datahttp://平台IP:驱动端口/device/data 方式访问.

注: 通过平台 303031000 端口访问 http server 驱动接口时, 必须添加 /http-server-driver 前缀. 如果平台为多项目版本时, 必须在请求头中添加 x-request-project, 值为 该驱动实例所在项目的ID.

脚本说明

脚本语言: JavasScript ECMAScript 5.1

驱动使用时要求提供  数据处理脚本 脚本函数来处理接收客户端推送过来的数据.

除此之外, 还内置了 lodash, crypto-js, moment, xml-jsformulajs(Excel函数) 包.

注: 所有的脚本的函数名必须为 handler.

表上下文

每个表有一个独立的上下文对象, 用于在脚本中存储一些数据, 这些数据会在该表的所有脚本中共享. 上下文对象提供以下方法:

put

用于向上下文中存储数据.

注: 存入的数据需要自行清理, 否则可能导致 OOM 等问题.

参数说明
参数名参数类型参数说明
keystring数据项的 key
valueany数据项的值
返回值

示例
function handler(context, request) {
// 向上下文中存储一个字符串
context.put("string", "this is a string");
// 向上下文中存储一个数值
context.put("number", 3.141);
// 向上下文中存储一个对象
context.put("object", {name: "张三", age: 18});
}

containsKey

判断上下文中是否存在指定的 key

参数说明
参数名参数类型参数说明
keystring数据项的 key
返回值

bool. true 表示 key 存在, false 表示 key 不存在

示例
function handler(context, request) {
// 向上下文中存储一个字符串
context.put("string", "this is a string");

// 返回 true
context.containsKey("string");
// 返回 false
context.containsKey("string1");
}

get

从上下文中获取指定的 key 对应的数据. 如果 key 不存在则返回 undefined

参数说明
参数名参数类型参数说明
keystring数据项的 key
返回值

anyundefined. 返回 put 时写入的数据.

示例
function handler(context, request) {
// 向上下文中存储一个字符串
context.put("string", "this is a string");

// 返回 "this is a string"
context.get("string");

// 返回 undefined
context.containsKey("string1");
}

getAndRemove

从上下文中获取指定的 key 对应的数据并且在返回后 删除key. 如果 key 不存在则返回 undefined.

注: 该函数返回后, 再使用 getgetAndRemove 均返回 undefined.

参数说明
参数名参数类型参数说明
keystring数据项的 key
返回值

anyundefined. 返回 put 时写入的数据.

示例
function handler(context, request) {
// 向上下文中存储一个字符串
context.put("string", "this is a string");

// 返回 "this is a string"
context.getAndRemove("string");

// 返回 undefined
context.get("string");

// 返回 undefined
context.getAndRemove("string");
}

remove

从上下文中删除指定的 key, 如果 key 不存在则不执行任何操作.

参数说明
参数名参数类型参数说明
keystring数据项的 key
返回值

示例
function handler(context, request) {
// 向上下文中存储一个字符串
context.put("string", "this is a string");

// 数据被删除
context.remove("string");

// 不执行任何操作
context.remove("string");
}

reportCommand

上传指令执行结果. 有些场景指令的执行结果是异步通知的, 或者指令通知与实时数据上传为同一接口. 此时,

可以通过 reportCommand 上报指令执行结果.

参数说明
参数名参数类型参数说明
serialNostring平台指令序号, 每次下发指令的序号不同
statestring指令最终执行状态. 可取值为 success, failure 和 pending 分别代表执行成功, 失败未完成
resultstring指令执行结果或失败说明
返回值

示例
function handler(context, request) {
// 向平台上报指令 "cmd_001" 执行结果, 状态为 "success", 结果为 "成功"
context.reportCommand("cmd_001", "success", "成功");

// 向平台上报指令 "cmd_001" 执行结果, 状态为 "failure", 失败原因为 "设备不可用"
context.reportCommand("cmd_001", "failure", "设备不可用");
}

数据处理脚本

该脚本用于处理从客户端发送的数据, 然后将数据解析为平台规定的格式并返回.

函数定义如下:


/**
* /**
* 数据处理脚本, 用于解析客户端推送过来的数据
* @param {Object} context 表上下文
* @param {Object} request 请求对象, 可以获取请求的相关信息
* @returns {{result: [{values: {}, id: string, time: number}], headers: {}, message: string, statusCode: number}}
*/
function handler(context, request) {
// 数据包处理逻辑
// statusCode 返回给客户端的状态码
// message 返回给客户端的响应数据
// headers 响应头
// result 从请求中解析出来的数据, 要求必须为数组. 如果请求不是上报实时采集数据时, 返回空数组即可.
return {
"statusCode": 200,
"message": "ok",
"headers": {"Content-Type": "application/json"},
"result": [{
"id": deviceId,
"time": timestamp,
"values": {"SignalPower": SignalPower, "SNR": SNR, "TxPower": TxPower}
}]
};
}
参数说明
参数名参数类型参数说明
contextobject表上下文
requestobject请求对象
urlstring请求 url
methodstring请求方式, 例如: GET, POST, PUT 等
remoteAddrstring客户端地址
pathstring请求路径
headersobject请求头信息. 注: 值为字符串数组
cookiesobjectcookie 信息. 注: 值为字符串数组
pathVariablesobject路径参数. 注: 值为字符串数组
queryobjecturl 中的请求参数. 注: 值为字符串数组
rawQuerystringurl 中参数部分内容
bodystring请求体
示例

curl 命令请求为例, 说明 request 中各字段的含义, 表注册的 path/demo/{deviceId}

curl -XPOST -H "x-request-project:62c4f8aaa0f974f96cca7ddc" -H "Content-Type:application/json" -d '{"deviceId":"2d1f1a708b5d4cef880937d67b5e5842","IMEI":"","IMSI":"","deviceType":"","tenantId":"1","productId":"1503","messageType":"dataReport","topic":"v1/up/ad","assocAssetId":"","timestamp":1528183784371,"payload":{"SignalPower":-792,"SNR":-55,"TxPower":50,"CellId":66966098,"Length":3,"Updata":"REVG"},"upPacketSN":-1,"upDataSN":-1,"serviceId":"","protocol":"tup"}' 'http://localhost:9505/demo/123456?a=123&b=abc'
字段名数据类型
urlstring/demo/123456?a=123&b=abc
methodstringPOST
remoteAddrstring[::1]:62298
pathstring/demo/123456
headersobject{"Accept": ["/"],"Content-Length": ["368"],"Content-Type": ["application/json"],"Cookie": ["Idea-7de7665d=c0b98b36-3396-41dd-b3a2-84e996a7155c"],"User-Agent": ["curl/7.68.0"],"X-Request-Prooject ": ["62 c4f8aaa0f974f96cca7ddc "]}
cookiesobject{"Idea-7de7665d":"c0b98b36-3396-41dd-b3a2-84e996a7155c"}
pathVariablesobject{"deviceId":"123456"}
queryobject{"a":["123"],"b":["abc"]}
rawQuerystringa=123&b=abc
bodystring{"deviceId":"2d1f1a708b5d4cef880937d67b5e5842","IMEI":"","IMSI":"","deviceType":"","tenantId":"1","productId":"1503","messageType":"dataReport","topic":"v1/up/ad","assocAssetId":"","timestamp":1528183784371,"payload":{"SignalPower":-792,"SNR":-55,"TxPower":50,"CellId":66966098,"Length":3,"Updata":"REVG"},"upPacketSN":-1,"upDataSN":-1,"serviceId":"","protocol":"tup"}

注: 部分请求头信息由 curl 命令自动添加.

返回值说明
参数名参数类型必填参数说明示例值
statusCodenumber返回给客户端的 http 响应码200
messagestring返回给客户端的响应内容"success"
headersobject返回给客户端的响应头{"Content-Type": "application/json", "custom-header": ["value1", "value2"]}
resultobject[]在请求中解析得到的实时数据[{"id":"d01","time":1665999863637,"values":{"temperature":17.5,"humidity":35.7}}]
.idstring资产编号或设备标识d01
.timenumber时间戳(ms)1664256913000
.valuesobject数据点信息{"temperature":17.5,"humidity":35.7}

注: 响应头的值可以为 stringArray<string> 两种类型。

示例
function handler(context, request) {
// 以请求体内容为 json 格式为例, 例如: {"id":"d01","time":"2022-10-17 17:57:32","values":[{"name":"temperature","data":17.5},{"name":"humidity","data":35.7}]}

// 示例, 从表上下文中获取数据
const valueFromContext = context.get("id");

// 获取请求体的内容并转换为 json 对象
const jsonData = JSON.parse(request.body);
// 从对象中提取时间信息
const time = moment(jsonData.time, "YYYY-MM-DD HH:mm:ss");

// 将 values 字段解析为平台规定的格式 {"key1": value1, "key2": "value2", ...}
const values = {};
for (let i = 0; i < jsonData.values.length; i++) {
const value = jsonData.values[i];
values[value.name] = value.data;
}

// 响回给客户端的响应码为 200
// 响应内容为 success
// 从请求中解析得到的实时数据为 [{id: jsonData.id, values: values, time: time.valueOf()}]
return {
"statusCode": 200,
"headers": {"Content-Type": "application/json"},
"message": "success",
"result": [{id: jsonData.id, values: values, time: time.valueOf()}]
};
}

指令处理脚本

HTTP Server 驱动是通过 http 请求实现指令下发. 当下发指令时, 驱动会将指令内容传递给 指令处理脚本.

由脚本从指令中解析出要调用的目标 http server 以及传递哪些参数、请求头和请求体信息, 然后驱动会根据这些信息最终发起请求.

指令脚本中包含两个函数, 分别为 handlercallback.

  • handler 用于解析平台指令, 并转化为请求信息
  • callback 用于处理平台发送指令请求后响应处理, 可实现从响应信息提取出指令执行结果上报给平台. **注: callback 为可选函数,

未定义时直接将指令请求响应结果作为指令结果**

有些系统指令执行结果的反馈是异步通知的, 或者目标系统有自己的指令标识. 因此需要将目标系统中的指令标识与平台的指令序号建立映射关系,

或者在接收到异步通知时再将执行结果上报给平台.

此时, 需要使用 callback 函数和 表上下文 来实现.

指令处理流程:

  1. 手动触发指令下发
  2. 驱动接收到指令下发请求时, 会调用 handler 函数.
  3. 根据 handler 返回的请求信息, 发起请求
  4. 如果定义了 callback 函数, 则将 步骤3 中的响应信息交给 callback 处理
  5. 上报平台指令处理结果

注: 需要在指令配置中的 数据写入 部分配置自定义表单, 然后在发送指令时填写发送内容, 在脚本中 command 参数即为填写的内容.

函数定义如下:

/**
* 指令处理脚本, 将指令内容解析为 http 请求数据格式.
*
* @param {Object} context 表上下文
* @param {string} serialNo 平台指令序号, 每个指令都有唯一的序号
* @param {string} tableId 设备所属表的标识
* @param {string} deviceId 设备标识
* @param {Object} command 指令信息
* @return {Object} 请求信息
*/
function handler(context, serialNo, deviceId, command) {
// 解析指令内容, 并返回请求相关信息
return {
url: `http://192.168.100.123/command/${deviceId}?time=${new Date().getTime()}`,
method: 'POST',
headers: {
"Content-Type": "application/json"
},
options: {
"timeout": 15000,
},
body: "the request body"
};
}

/**
* 指令请求回调函数. 接收 handler 函数对应请求的响应信息, 并转换为平台指令响应结果
*
* @param {Object} context 表上下文
* @param {string} serialNo 平台指令序号, 每个指令都有唯一的序号
* @param {string} tableId 设备所属表的标识
* @param {string} deviceId 设备标识
* @param {Object} command 指令信息
* @returns {{result: string, state: string}}
*/
function callback(context, tableId, deviceId, serialNo, response) {
// 根据响应信息执行相关操作
return {state: "success", result: "指令返回结果"};
}
handler 参数说明
参数名参数类型参数说明
contextobject表上下文
serialNostring平台指令序号, 每个指令都有唯一的序号
tableIdstring设备所属表的标识
deviceIdstring自定义设备标识
commandobject指令信息, 格式如下

指令格式如下:

{
"name": "demo",
"showName": "示例指令",
"params": {
"demo": {
"productId": 100123,
"operator": "张三",
"ttl": 7200,
"payload": [
{
"key": "status",
"value": "1"
},
{
"key": "temperature",
"value": "26"
}
]
}
}
}
字段名参数类型参数说明
name字符串指令名称
showName字符串指令显示名称
params对象数据写入配置, 格式由 数据写入 的配置决定
defaultValue对象数据写入配置中各字段的默认值
handler 返回值说明
字段名参数类型必填参数说明示例值
urlstring请求地址http://192.168.100.123:8080/command/sn10032?seq=000123&delay=0
methodstring请求方式可以为 GET, POST, PUT
headersobject请求头{"Content-Type": "application/json", "Custom-Header": ["value1", "value2"]}
optionsobject配置项{"Content-Type": "application/json", "Custom-Header": ["value1", "value2"]}
timeoutnumber请求超时(ms)单位: ms, 默认: 15000
optionsobject配置项{"Content-Type": "application/json", "Custom-Header": ["value1", "value2"]}
bodystringobject请求体
callback 参数说明
参数名参数类型参数说明
contextobject表上下文
serialNostring平台指令序号, 每个指令都有唯一的序号
tableIdstring设备所属表的标识
deviceIdstring自定义设备标识
responseobject响应信息, 格式如下

响应信息格式如下:

{
"statusCode": 200,
"headers": {
"Content-Type": "application/json"
},
"body": "消息体"
}
字段名参数类型参数说明
statusCodenumber响应码. 例如: 200
headersobject响应头. 例如: {"Content-Type": "application/json"}
bodystring消息体. 例如: {"code": 0, "message": "success"}
callback 返回值说明
字段名参数类型必填参数说明示例值
statestring指令执行状态枚举值. success, failure, pending
resultstring指令执行结果成功
callback state 枚举状态说明
说明
success指令执行成功
failure指令执行失败
pending指令未完成. 用于异步反馈指令执行结果情况
示例1

该示例, 未定义 callback 函数. 驱动直接使用请求的响应结果作为执行结果.

如果响应码为 200, 表示指令执行成功, 消息体作为指令执行结果.

function handler(context, serialNo, deviceId, command) {
const {productId, operator, ttl, payload} = command.params.demo;
const content = {};
for (let i = 0; i < payload.length; i++) {
const data = payload[i];
content[data.name] = data.value;
}

return {
url: "http://192.168.50.12:8080/command",
method: "POST",
headers: {
"Content-Type": "application/json"
},
options: {
"timeout": 15000
},
body: {
"deviceId": deviceId,
"productId": productId,
"operator": operator,
"ttl": ttl,
"content": {
payload: content
}
}
};
}
示例2

该示例, 使用 callback 从响应信息中提取指令执行结果信息.

function handler(context, serialNo, deviceId, command) {
const {productId, operator, ttl, payload} = command.params.demo;
const content = {};
for (let i = 0; i < payload.length; i++) {
const data = payload[i];
content[data.name] = data.value;
}

return {
url: "http://192.168.50.12:8080/command",
method: "POST",
headers: {
"Content-Type": "application/json"
},
options: {
"timeout": 15000
},
body: {
"deviceId": deviceId,
"productId": productId,
"operator": operator,
"ttl": ttl,
"content": {
payload: content
}
}
};
}

/**
* 从响应信息中提取指令执行结果.
*/
function callback(context, serialNo, deviceId, response) {
// 例如, 消息体内容为 {"code": 200, "message": "success"}
const {code, message} = JSON.parse(response.body);
return {state: code == 200 ? "success" : "failure", result: "message"};
}
示例3

该示例中, 假设指令执行结果为异步通知, 并且目标系统有独立的指令标识

function handler(context, serialNo, deviceId, command) {
const {productId, operator, ttl, payload} = command.params.demo;
const content = {};
for (let i = 0; i < payload.length; i++) {
const data = payload[i];
content[data.name] = data.value;
}

return {
url: "http://192.168.50.12:8080/command",
method: "POST",
headers: {
"Content-Type": "application/json"
},
options: {
"timeout": 15000
},
body: {
"deviceId": deviceId,
"productId": productId,
"operator": operator,
"ttl": ttl,
"content": {
payload: content
}
}
};
}

function callback(context, serialNo, deviceId, response) {
// 例如, 消息体内容为 {"commandId": "cmd_001"}
const {commandId} = JSON.parse(response.body);

// 从消息体中提取出目标系统的指令标识, 并与平台指令序号建立映射关系
context.put(commandId, serialNo);

// 返回 state 为 pending, 表示指令未完成
return {state: "pending", result: "处理中"};
}


/**
* 数据处理脚本. 接收到目标系统推送的指令执行结果. 此时处理未完成的指令并上报到平台
*/
function handler(context, request) {
// 根据请求中的一些信息判断该请求为指令执行结果通知请求
if (request.headers["type"] === "command") {
// 从请求消息体中提取出命令标识以及指令执行结果
// 例如, 消息体格式为 {"commandId": "cmd_001", "code": 200, "message": "success"}
const {commandId, code, message} = JSON.parse(request.body);
// 根据目标系统的指令标识获取平台指令序号
const serialNo = context.getAndRemove(commandId);
// 通过表上下文上报指令执行结果
context.reportCommand(serialNo, code === 200 ? "success" : "failure", message);

// 返回空对象
return {};
}
}